namespace TwitterButtler.ViewModels
{
	#region #using Directives

	using System;
	using System.Collections.ObjectModel;
	using System.Diagnostics;
	using System.Net;
	using System.Windows.Input;
	using System.Windows.Media;
	using System.Windows.Media.Imaging;

	using ContinuousLinq.Reactive;

	using Helpers;

	using TweetSharp;

	#endregion

	internal class TwitterButtlerViewModel : ReactiveObject,
	                                         IDisposable
	{
		private readonly OAuthRequestToken requestToken;
		private readonly TimelineWatcher timelineWatcher;
		private readonly TwitterService twitterService;
		private bool isBusy;
		private bool isOnline;
		private ICommand logInCommand;
		private ObservableCollection<string> timelineItems = new ObservableCollection<string>();
		private ImageSource timelineProfileBackground;
		private Brush timelineProfileBackgroundColor;
		private ImageSource timelineProfileImage;
		private Brush timelineProfileTextColor;
		private string twitterErrorMessage;
		private string twitterPin;
		private TwitterUser twitterUser;

		static TwitterButtlerViewModel()
		{
			DependsOn<TwitterButtlerViewModel> dependsOn = Register<TwitterButtlerViewModel>();

			dependsOn.Call(me => CommandManager.InvalidateRequerySuggested()).OnChanged(me => me.TwitterPin);

			dependsOn.Call(me => me.OnTwitterProfileChanged()).OnChanged(me => me.TwitterUser);
		}

		public TwitterButtlerViewModel(TimelineWatcher timelineWatcher)
		{
			if (timelineWatcher == null)
			{
				throw new ArgumentNullException("timelineWatcher");
			}

			this.twitterService = new TwitterService("<YourConsumerKey>", "<YourConsumerSecret>");

			this.requestToken = this.twitterService.GetRequestToken();

			Uri AuthURL = this.twitterService.GetAuthorizationUri(this.requestToken);

			Process.Start(AuthURL.AbsoluteUri); // Launch browser with authorization URL.

			this.timelineWatcher = timelineWatcher;
			this.timelineWatcher.TwitterService = this.twitterService;

			LogInCommand = new RelayCommand(OnLogInCommand, CanLogIn);

			OnTwitterProfileChanged();

			TimelineItems = this.timelineWatcher.TimelineItems;
		}

		#region Public Properties

		public bool IsBusy
		{
			[DebuggerStepThrough]
			get { return this.isBusy; }
			set
			{
				if (value == IsBusy)
				{
					return;
				}

				this.isBusy = value;

				OnPropertyChanged("IsBusy");
			}
		}

		public string TwitterPin
		{
			[DebuggerStepThrough]
			get { return this.twitterPin; }
			set
			{
				if (value == TwitterPin)
				{
					return;
				}

				this.twitterPin = value;

				OnPropertyChanged("TwitterPin");
			}
		}

		public ICommand LogInCommand
		{
			[DebuggerStepThrough]
			get { return this.logInCommand; }
			set
			{
				if (value == LogInCommand)
				{
					return;
				}

				this.logInCommand = value;

				OnPropertyChanged("LogInCommand");
			}
		}

		public bool IsOnline
		{
			[DebuggerStepThrough]
			get { return this.isOnline; }
			set
			{
				if (value == IsOnline)
				{
					return;
				}

				this.isOnline = value;

				OnPropertyChanged("IsOnline");
			}
		}

		public string TwitterErrorMessage
		{
			[DebuggerStepThrough]
			get { return this.twitterErrorMessage; }
			set
			{
				if (value == TwitterErrorMessage)
				{
					return;
				}

				this.twitterErrorMessage = value;

				OnPropertyChanged("TwitterErrorMessage");
			}
		}

		public ObservableCollection<string> TimelineItems
		{
			[DebuggerStepThrough]
			get { return this.timelineItems; }
			set
			{
				if (value == TimelineItems)
				{
					return;
				}

				this.timelineItems = value;

				OnPropertyChanged("TimelineItems");
			}
		}

		public ImageSource TimelineProfileBackground
		{
			[DebuggerStepThrough]
			get { return this.timelineProfileBackground; }
			set
			{
				if (value == TimelineProfileBackground)
				{
					return;
				}

				this.timelineProfileBackground = value;

				OnPropertyChanged("TimelineProfileBackground");
			}
		}

		public ImageSource TimelineProfileImage
		{
			[DebuggerStepThrough]
			get { return this.timelineProfileImage; }
			set
			{
				if (value == TimelineProfileImage)
				{
					return;
				}

				this.timelineProfileImage = value;

				OnPropertyChanged("TimelineProfileImage");
			}
		}

		public Brush TimelineProfileTextColor
		{
			[DebuggerStepThrough]
			get { return this.timelineProfileTextColor; }
			set
			{
				if (value == TimelineProfileTextColor)
				{
					return;
				}

				this.timelineProfileTextColor = value;

				OnPropertyChanged("TimelineProfileTextColor");
			}
		}

		public Brush TimelineProfileBackgroundColor
		{
			[DebuggerStepThrough]
			get { return this.timelineProfileBackgroundColor; }
			set
			{
				if (value == TimelineProfileBackgroundColor)
				{
					return;
				}

				this.timelineProfileBackgroundColor = value;

				OnPropertyChanged("TimelineProfileBackgroundColor");
			}
		}

		#endregion

		#region Private Properties

		private TwitterUser TwitterUser
		{
			[DebuggerStepThrough]
			get { return this.twitterUser; }
			set
			{
				if (value == TwitterUser)
				{
					return;
				}

				this.twitterUser = value;

				OnPropertyChanged("TwitterUser");
			}
		}

		#endregion

		#region Event Handlers

		private bool CanLogIn(object obj)
		{
			return !string.IsNullOrEmpty(TwitterPin);
		}

		private void OnLogInCommand(object obj)
		{
			var accessToken = this.twitterService.GetAccessToken(this.requestToken, TwitterPin.Trim());

			this.twitterService.AuthenticateWith(accessToken.Token, accessToken.TokenSecret);

			IsBusy = true;

			this.twitterService.VerifyCredentials(OnCredentialsVerifiedCallback);
		}

		private void OnCredentialsVerifiedCallback(TwitterUser user, TwitterResponse r)
		{
			if (!Dispatcher.CheckAccess())
			{
				Dispatcher.Invoke(new Action(() => OnCredentialsVerifiedCallback(user, r)));
				return;
			}

			string error = r.StatusDescription;

			TwitterErrorMessage = error;
			TwitterUser = (r.StatusCode == HttpStatusCode.OK ? user : null);

			IsBusy = false;
		}

		private void OnTwitterProfileChanged()
		{
			IsOnline = (TwitterUser != null);

			if (TwitterUser == null)
			{
				TimelineProfileBackground = null;
				TimelineProfileImage = null;
				TimelineProfileTextColor = Brushes.DarkGray;
				TimelineProfileBackgroundColor = Brushes.White;
				return;
			}

			var brushConverter = new BrushConverter();

			TimelineProfileBackground = new BitmapImage(new Uri(TwitterUser.ProfileBackgroundImageUrl, UriKind.Absolute));
			TimelineProfileImage = new BitmapImage(new Uri(TwitterUser.ProfileImageUrl, UriKind.Absolute));
			TimelineProfileTextColor = (Brush) brushConverter.ConvertFrom("#" + TwitterUser.ProfileTextColor);
			TimelineProfileBackgroundColor = (Brush) brushConverter.ConvertFrom("#" + TwitterUser.ProfileBackgroundColor);
		}

		public void OnAccountBoxKeyDown(object sender, KeyEventArgs args)
		{
			if (args.Key == Key.Enter)
			{
				LogInCommand.Execute(null);
				args.Handled = true;
			}
		}

		public void OnPasswordBoxKeyDown(object sender, KeyEventArgs args)
		{
			if (args.Key == Key.Enter)
			{
				LogInCommand.Execute(null);
				args.Handled = true;
			}
		}

		#endregion

		~TwitterButtlerViewModel()
		{
			Dispose(false);
		}

		#region Implementation of IDisposable

		/// <summary>
		/// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.
		/// </summary>
		/// <filterpriority>2</filterpriority>
		public void Dispose()
		{
			Dispose(true);
			GC.SuppressFinalize(this);
		}

		private void Dispose(bool disposing)
		{
			if (disposing)
			{
				if (this.timelineWatcher != null)
				{
					this.timelineWatcher.Dispose();
				}
			}
		}

		#endregion
	}
}